#include "global_declarations.h"
#include "stdlib.h"
#include "string.h"
#include "ctype.h"
#include "stdio.h"
#include "misc.h"
#include "../DEHT/deht.h"
#include "assert.h"

/**
 * checks that the file exists and accessible.
 */
bool check_file(char* file_name) {
	FILE* f;
	f = fopen(file_name, "r");
	if (f == NULL) {
		perror(file_name);
		return false;
	}
	fclose(f);
	return true;
}

/**
 * returns true if file exists and prints an error message.
 */
bool check_file_exist(char* file_name) {
	FILE* f = fopen(file_name, "r");
	if (f == NULL)
		return false;
	fprintf(stderr, "Error: File %s already exist\n", file_name);
	fclose(f);
	return true;
}

/**
 * returns a pointer to the corresponding function.
 * @pre: str == "SHA1" or "MD5".
 */
BasicHashFunctionPtr which_hash(char* str) {
	if (strcmp(str, "MD5") == 0)
		return MD5BasicHash;
	if (strcmp(str, "SHA1") == 0)
		return SHA1BasicHash;
	return 0;
}

/**
 * checks that the hash name is either SH1, MD5.
 */
bool check_Hash_name(char* s) {
	if (strcmp(s, "MD5") == 0 || strcmp(s, "SHA1") == 0)
		return true;
	fprintf(stderr, "Error: Hash %s is not supported\n", s);
	return false;
}

/**
 * returns the output length of the corresponding hash.
 * @pre: str == "SHA1" or "MD5".
 */
int hash_Length(char* str) {
	if (strcmp(str, "MD5") == 0)
		return MD5_OUTPUT_LENGTH_IN_BYTES;
	if (strcmp(str, "SHA1") == 0)
		return SHA1_OUTPUT_LENGTH_IN_BYTES;
	return 0;
}

/**
 * hashes the word and puts in dest the hexa value of the hashed word.
 */
void get_hexadecimal_hashed_password(char* word, BasicHashFunctionPtr hash,
		char* dest) {
	unsigned char temp[MAX_LINE_LENGTH] = {0};
	hash((unsigned char*) word, strlen(word), temp);
	binary2hexa(temp, strlen((char*) temp), dest, MAX_LINE_LENGTH);
}

void get_hash_name_from_file(FILE* f, char* dest) {
	fgets(dest, 20, f);
	remove_end_of_line(dest);
}

/**
 * hashes the word and puts in dest the hexa value of the hashed word.
 */
void get_salty_hexadecimal_hashed_password(char* word, unsigned char* salt,
		BasicHashFunctionPtr hash, int hash_length, char* dest) {
	int l;
	char temp[MAX_LINE_LENGTH] = { 0 };
	char temp2[MAX_LINE_LENGTH] = { 0 };
	l = strlen(word);
	strcpy(temp, word);
	memcpy(temp + l, (char*) salt, 6);
	hash((unsigned char*) temp, l + 6, (unsigned char*) temp2);
	binary2hexa((unsigned char*) temp2, hash_length, dest, MAX_LINE_LENGTH);
}

/**
 * checks whether c equals delimiter.
 * if delimiter == 0. checks whether c is any space.
 */
bool is_delimiter(char c, char delimiter){
	if (delimiter == 0)
		return (isspace((int)c));
	return (c == delimiter);
}

/**
 * counts the number of words in the array input.
 * if delimiter is 0 the function will consider every space as delimiter
 */
int count_words_in_line(char* line, char delimiter) {
	int wordsCounter;
	char lastChar = delimiter;
	int i, l;
	if (line == 0)
		return 0;
	l = strlen(line);
	wordsCounter = 0;
	for (i = 1; i <= l; ++i) {
		if (line[0] == '\n')
			return wordsCounter;
		if (!is_delimiter(line[0], delimiter)) {
			if (is_delimiter(lastChar, delimiter)) {
				wordsCounter++;
			}
		}
		lastChar = line[0];
		line++;
	}
	return wordsCounter;
}

/**
 * returns a pointer to the index's word in the input.
 * the first word's index is numbered 1.
 */
char* get_word_num_ptr(char* input, int index, char delimiter) {
	int wordsCounter;
	char lastChar = delimiter;
	int i, inputLength;
	if (input == 0)
		return 0;
	inputLength = strlen(input);
	wordsCounter = 0;
	for (i = 1; i <= inputLength; ++i) {
		if (!is_delimiter(input[0], delimiter)) {
			if (is_delimiter(lastChar, delimiter)) {
				wordsCounter++;
				if (wordsCounter == index)
					return input;
			}
		}
		lastChar = *input;
		input++;
	}
	return input;
}

/**
 * returns the number of figures until the next tub or null.
 */
int get_word_length(char* input, char delimiter) {
	char cur;
	int counter;
	counter = 0;
	if (input == 0)
		return 0;
	cur = input[0];
	while ((cur != 0) && !(is_delimiter(cur, delimiter))) {
		counter++;
		cur = input[counter];
	}
	return counter;
}

/**
 * copies the desired word to dest.
 * dest must be allocated and can contain the desired word.
 */
void get_word_num(char* input, int index, char* dest, char delimiter) {
	char* word;
	int length;
	word = get_word_num_ptr(input, index, delimiter);
	length = get_word_length(word, delimiter);
	strncpy(dest, word, length);
}

/**
 * puts in dest the value of the key according to the file.
 * returns -1 if the key is not in the file.
 */
int get_value(char* key, FILE* f, char* dest, char delimiter) {
	char word1[MAX_LINE_LENGTH] = { 0 };
	char first_line[20] = { 0 }; /*we don't want to scan the first line of the file (MD5/SHA1).*/
	char line[MAX_LINE_LENGTH] = { 0 };
	assert(f != NULL);
	fseek(f, 0, SEEK_SET);
	fgets(first_line, sizeof(first_line), f);
	while (fgets(line, sizeof(line), f) != NULL) {
		remove_end_of_line(line);
		get_word_num(line, 1, word1, delimiter);
		get_word_num(line, 2, dest, delimiter);
		if (strcmp(word1, key) == 0)
			return 1;
		memset(word1, 0, MAX_LINE_LENGTH);
		memset(dest, 0, MAX_LINE_LENGTH);
	}
	return -1;
}

int validate_hex_string(char* line) {
	char* p = line;
	while (*p != 0 && *p != '\n') {
		switch (*p) {
		case '1':
		case '2':
		case '3':
		case '4':
		case '5':
		case '6':
		case '7':
		case '8':
		case '9':
		case '0':
		case 'a':
		case 'b':
		case 'c':
		case 'd':
		case 'e':
		case 'f':
			p++;
			continue;
		default:
			return false;
		}
	}
	return true;
}

/**
 * checks whether there exists deht files (.key, .data) with the given name.
 */
bool check_deht_files_exists(char* name) {
	char* suffix1;
	char* suffix2;
	char temp1[MAX_LINE_LENGTH] = { 0 };
	char temp2[MAX_LINE_LENGTH] = { 0 };;
	suffix1 = ".data";
	suffix2 = ".key";
	strcpy(temp1, name);
	strcat(temp1, suffix1);
	strcpy(temp2, name);
	strcat(temp2, suffix2);
	if (check_file_exist(temp1) || check_file_exist(temp1))
		return true;
	return false;
}
